package is.web;

import is.model.bean.AbstractHierarchyBean;
import is.model.exceptions.CyclingHierarchyException;
import java.util.LinkedList;
import java.util.List;

/**
 *
 * @author aspr0
 */
public class Hierarchy<Datatype extends AbstractHierarchyBean>
{
    private List<Datatype> beans; // <Dt> use caused by List<Bean> to List<Dt> cast error
    private List<Datatype> parentBeans; // <Dt> use caused by List<Bean> to List<Dt> cast error

    public void setBeans(List<Datatype> beans)
    {
        this.beans = beans;
    }

    public List<Datatype> build()
    {
        List<Datatype> beansNesting = new LinkedList<Datatype>();

        LinkedList<Integer> branchIds = new LinkedList<Integer>();

        /* Starting from parentId = 0 root */
        Integer searchId = 0;
        Integer nesting = 0;

        while (beans.size() > 0)
        {
            Datatype bean = removeChildBean(searchId);

            /* Branch found */
            if (bean != null)
            {
                bean.setNesting(nesting);
                beansNesting.add(bean);

                /* Branch has child branches */
                if (childBeanExists(bean.getId()))
                {
                    /* Going deeper */
                    nesting++;
                    branchIds.add(searchId);
                    searchId = bean.getId();
                }
            }
            /* Branch has no child branches */
            else
            {
                /* One step back to root */
                searchId = branchIds.removeLast();
                nesting--;
            }
        }

        beans = beansNesting;

        return beans;
    }

    /**
     * May be applied only after hierarchy is built.
     *
     * @param bean
     * @return
     */
    public List<Datatype> excludeChild(Datatype branchRootBean)
    {
        parentBeans = new LinkedList<Datatype>();

        Boolean inBranch = false;
        /* init value's for java-checker only. It isn't used outside branch */
        Integer rootNesting = 0;
        Integer rootId = branchRootBean.getId();

        for (Datatype bean : beans)
        {
            if (bean.getId() == rootId)
            {
                inBranch = true;
                rootNesting = bean.getNesting();
            }

            if (inBranch)
            {
                if (bean.getId() != rootId && bean.getNesting() <= rootNesting)
                {
                    /* branch ended */
                    inBranch = false;
                }
            }

            if (!inBranch)
            {
                parentBeans.add(bean);
            }
        }

        return parentBeans;
    }

    public void proveParent(Datatype bean) throws CyclingHierarchyException
    {
        if (bean.getParentId() == null)
        {
            return;
        }

        for (Datatype branchBean : parentBeans)
        {
            if (branchBean.getId().equals(bean.getParentId()))
            {
                return;
            }
        }

        throw new CyclingHierarchyException();
    }

    private Datatype removeChildBean(Integer parentId)
    {
        for (Datatype bean : beans)
        {
            if (bean.getParentId() == parentId)
            {
                beans.remove(bean);
                return bean;
            }
        }

        return null;
    }

    private Boolean childBeanExists(Integer parentId)
    {
        for (Datatype bean : beans)
        {
            if (bean.getParentId() == parentId)
            {
                return true;
            }
        }

        return false;
    }
}
